#include "stdafx.h"
#include <serialization/ioParamStructs.h>
#include <serialization/serialization.h>
#include <winsock/winsock_stub.h>

#include <serialization/addrinfo.h>
#include <set>

std::set<PADDRINFOA>  allocatedStubAddInfoAs;
std::set<PADDRINFOW> allocatedStubAddInfoWs;

void getaddrinfo_stub (
    __in  std::vector<char>& in_buf,
    __out std::vector<char>& out_buf
    )
{
//RECEIVE
    Deserialize deserialize (in_buf);
    getaddrinfo_in_inout getaddrinfoIN;
    deserialize (getaddrinfoIN);

    ADDRINFOA* pHints = NULL;
    CreateAddrinfoFromStdList (getaddrinfoIN.pHints_val_, &pHints);
    ADDRINFOA* ppResult = NULL;

    int ret = getaddrinfo (getaddrinfoIN.pNodeName_.c_str(), 
                           getaddrinfoIN.pServiceName_.c_str(),
                           pHints, &ppResult);
    
    allocatedStubAddInfoAs.insert(ppResult);
    DestroyAddrinfoFromStdList (pHints);
//SEND
    getaddrinfo_out_inout getaddrinfoOUT( ret, ppResult, WSAGetLastError() );
    
    Serialize(deserialize.get_header(), getaddrinfoOUT, out_buf);
}

void GetAddrInfoW_stub (
    __in  std::vector<char>& in_buf,
    __out std::vector<char>& out_buf
    )
{
//RECEIVE
    Deserialize deserialize (in_buf);
    GetAddrInfoW_in_inout GetAddrInfoWIN;
    deserialize (GetAddrInfoWIN);

    ADDRINFOW* pHints = NULL;
    CreateAddrinfoFromStdList (GetAddrInfoWIN.pHints_val_, &pHints);
    ADDRINFOW* ppResult = NULL;

    int ret = GetAddrInfoW (GetAddrInfoWIN.pNodeName_.c_str(), 
                            GetAddrInfoWIN.pServiceName_.c_str(),
                            pHints, &ppResult);
    
    allocatedStubAddInfoWs.insert(ppResult);
    DestroyAddrinfoFromStdList (pHints);
//SEND
    GetAddrInfoW_out_inout GetAddrInfoWOUT( ret, ppResult, WSAGetLastError() );
    
    Serialize(deserialize.get_header(), GetAddrInfoWOUT, out_buf);
}

void freeaddrinfo_stub (
    __in  std::vector<char>& in_buf,
    __out std::vector<char>& out_buf
    )
{
//RECEIVE
    Deserialize deserialize (in_buf);
    freeaddrinfo_in_inout freeaddrinfoIN;
    deserialize (freeaddrinfoIN);

    std::set<PADDRINFOA>::iterator it;

    it = allocatedStubAddInfoAs.find( (ADDRINFOA*)freeaddrinfoIN.ai_ptr_ );
    if (it != allocatedStubAddInfoAs.end())
    {
        ADDRINFOA* ppResult = (ADDRINFOA*) *it;
        freeaddrinfo (ppResult);
        allocatedStubAddInfoAs.erase (it);
    }
//SEND
    freeaddrinfo_out_inout freeaddrinfoOUT;
    
    Serialize(deserialize.get_header(), freeaddrinfoOUT, out_buf);
}

void FreeAddrInfoW_stub (
    __in  std::vector<char>& in_buf,
    __out std::vector<char>& out_buf
    )
{
//RECEIVE
    Deserialize deserialize (in_buf);
    FreeAddrInfoW_in_inout FreeAddrInfoWIN;
    deserialize (FreeAddrInfoWIN);

    std::set<PADDRINFOW>::iterator it;

    it = allocatedStubAddInfoWs.find( (ADDRINFOW*)FreeAddrInfoWIN.ai_ptr_ );
    if (it != allocatedStubAddInfoWs.end())
    {
        ADDRINFOW* ppResult = (ADDRINFOW*) *it;
        FreeAddrInfoW (ppResult);
        allocatedStubAddInfoWs.erase (it);
    }
//SEND
    FreeAddrInfoW_out_inout FreeAddrInfoWOUT;
    
    Serialize(deserialize.get_header(), FreeAddrInfoWOUT, out_buf);
}

////////////////////////////////////////////////////////////
void ConvertToAddrinfoStdList (
    __in  const ADDRINFOA*                ai,
    __out std::list<ADDRINFOA_STD_LIST>&  ai_list
    )
{
    const ADDRINFOA*    ai_ptr = ai;
    ADDRINFOA_STD_LIST  current;
    
    while (ai_ptr)
    {
        current.ai_flags = ai_ptr->ai_flags;
        current.ai_family = ai_ptr->ai_family;
        current.ai_socktype = ai_ptr->ai_socktype;
        current.ai_protocol = ai_ptr->ai_protocol;
        current.ai_addrlen = ai_ptr->ai_addrlen;
        current.ai_canonname = 
            (ai_ptr->ai_canonname == NULL) ? "" : ai_ptr->ai_canonname;
        
        if (ai_ptr->ai_addr)
        {
            current.ai_addr_val.sa_family = ai_ptr->ai_addr->sa_family;
            strncpy_s (current.ai_addr_val.sa_data, 14, ai_ptr->ai_addr->sa_data, 14);
        } else {
            current.ai_addr_val.sa_family = -1;
        }

        ai_list.push_back(current);
        ai_ptr = ai_ptr->ai_next;
    }
}

////////////////////////////////////////////////////////////
void ConvertToAddrinfoStdList (
    __in  const ADDRINFOW*                ai,
    __out std::list<ADDRINFOW_STD_LIST>&  ai_list
    )
{
    const ADDRINFOW*    ai_ptr = ai;
    ADDRINFOW_STD_LIST  current;
    
    while (ai_ptr)
    {
        current.ai_flags = ai_ptr->ai_flags;
        current.ai_family = ai_ptr->ai_family;
        current.ai_socktype = ai_ptr->ai_socktype;
        current.ai_protocol = ai_ptr->ai_protocol;
        current.ai_addrlen = ai_ptr->ai_addrlen;
        current.ai_canonname = 
            (ai_ptr->ai_canonname == NULL) ? L"" : ai_ptr->ai_canonname;
        
        if (ai_ptr->ai_addr)
        {
            current.ai_addr_val.sa_family = ai_ptr->ai_addr->sa_family;
            strncpy_s (current.ai_addr_val.sa_data, 14, ai_ptr->ai_addr->sa_data, 14);
        } else {
            current.ai_addr_val.sa_family = -1;
        }

        ai_list.push_back(current);
        ai_ptr = ai_ptr->ai_next;
    }
}

////////////////////////////////////////////////////////////
void CreateAddrinfoFromStdList ( 
    __in  std::list<ADDRINFOA_STD_LIST>  ai_list,
    __out ADDRINFOA**                    ai
    )
{
    if (ai_list.size() == 0) {
        *ai = NULL;
        return;
    }

    *ai = new ADDRINFOA;
    ADDRINFOA*          ai_ptr = *ai;
    ADDRINFOA_STD_LIST* current;

    do
    {
        current = &ai_list.front();
        
        ai_ptr->ai_flags = current->ai_flags;
        ai_ptr->ai_family = current->ai_family;
        ai_ptr->ai_socktype = current->ai_socktype;
        ai_ptr->ai_protocol = current->ai_protocol;
        ai_ptr->ai_addrlen = current->ai_addrlen;
        if (current->ai_canonname == "")
        {
            ai_ptr->ai_canonname = NULL;
        } else {
            ai_ptr->ai_canonname = new char[current->ai_canonname.length()+1];
            strcpy_s (ai_ptr->ai_canonname, current->ai_canonname.length()+1,
                current->ai_canonname.c_str());
        }
        short tmp_cmp = current->ai_addr_val.sa_family;
        if (tmp_cmp != -1)
        {
            ai_ptr->ai_addr = new SOCKADDR;
            ai_ptr->ai_addr->sa_family = current->ai_addr_val.sa_family;
            strncpy_s (ai_ptr->ai_addr->sa_data, 14, current->ai_addr_val.sa_data, 14);
        } else {
            ai_ptr->ai_addr = NULL;
        }
        if (ai_list.size() != 1) {
            ai_ptr->ai_next = new ADDRINFOA;
        } else {
            ai_ptr->ai_next = NULL;
        }

        ai_ptr = ai_ptr->ai_next;
        ai_list.pop_front();        
    } while (!ai_list.empty());
}

////////////////////////////////////////////////////////////
void CreateAddrinfoFromStdList ( 
    __in  std::list<ADDRINFOW_STD_LIST>  ai_list,
    __out ADDRINFOW**                    ai
    )
{
    if (ai_list.size() == 0) {
        *ai = NULL;
        return;
    }

    *ai = new ADDRINFOW;
    ADDRINFOW*          ai_ptr = *ai;
    ADDRINFOW_STD_LIST* current;

    do
    {
        current = &ai_list.front();
        
        ai_ptr->ai_flags = current->ai_flags;
        ai_ptr->ai_family = current->ai_family;
        ai_ptr->ai_socktype = current->ai_socktype;
        ai_ptr->ai_protocol = current->ai_protocol;
        ai_ptr->ai_addrlen = current->ai_addrlen;
        if (current->ai_canonname == L"")
        {
            ai_ptr->ai_canonname = NULL;
        } else {
            ai_ptr->ai_canonname = new wchar_t[current->ai_canonname.length()+1];
            wcscpy_s (ai_ptr->ai_canonname, current->ai_canonname.length()+1,
                current->ai_canonname.c_str());
        }
        short tmp_cmp = current->ai_addr_val.sa_family;
        if (tmp_cmp != -1)
        {
            ai_ptr->ai_addr = new SOCKADDR;
            ai_ptr->ai_addr->sa_family = current->ai_addr_val.sa_family;
            strncpy_s (ai_ptr->ai_addr->sa_data, 14, current->ai_addr_val.sa_data, 14);
        } else {
            ai_ptr->ai_addr = NULL;
        }
        if (ai_list.size() != 1) {
            ai_ptr->ai_next = new ADDRINFOW;
        } else {
            ai_ptr->ai_next = NULL;
        }

        ai_ptr = ai_ptr->ai_next;
        ai_list.pop_front();        
    } while (!ai_list.empty());
}

////////////////////////////////////////////////////////////
void DestroyAddrinfoFromStdList ( 
    __in ADDRINFOA*  ai
    )
{
    ADDRINFOA*  ai_ptr = ai;

    if (ai == NULL) {
        return;
    }
    
    if (ai_ptr->ai_canonname != NULL)
    {
        delete[] ai_ptr->ai_canonname;
    }
    if (ai_ptr->ai_addr != NULL)
    {
        delete ai_ptr->ai_addr;
    }
    ADDRINFOA* tmp = ai_ptr;
    ai_ptr = ai_ptr->ai_next;
    delete tmp;
    
    while (ai_ptr)
    {
        if (ai_ptr->ai_canonname != NULL)
        {
            delete[] ai_ptr->ai_canonname;
        }
        if (ai_ptr->ai_addr != NULL)
        {
            delete ai_ptr->ai_addr;
        }

        ADDRINFOA* tmp = ai_ptr;
        ai_ptr = ai_ptr->ai_next;
        delete tmp; 
    }
    ai = NULL;
}

////////////////////////////////////////////////////////////
void DestroyAddrinfoFromStdList ( 
    __in ADDRINFOW*  ai
    )
{
    ADDRINFOW*  ai_ptr = ai;

    if (ai == NULL) {
        return;
    }
    
    if (ai_ptr->ai_canonname != NULL)
    {
        delete[] ai_ptr->ai_canonname;
    }
    if (ai_ptr->ai_addr != NULL)
    {
        delete ai_ptr->ai_addr;
    }
    ADDRINFOW* tmp = ai_ptr;
    ai_ptr = ai_ptr->ai_next;
    delete tmp;
    
    while (ai_ptr)
    {
        if (ai_ptr->ai_canonname != NULL)
        {
            delete[] ai_ptr->ai_canonname;
        }
        if (ai_ptr->ai_addr != NULL)
        {
            delete ai_ptr->ai_addr;
        }

        ADDRINFOW* tmp = ai_ptr;
        ai_ptr = ai_ptr->ai_next;
        delete tmp; 
    }
    ai = NULL;
}
